package bluetooth;

import java.io.IOException;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;

/**
 * Klasa implementujaca serwer bluetooth.
 * @author      Przemyslaw Bierut
 * @version     1.0
 */
public class Server implements BtListener {

    private String UUID;
    private StreamConnectionNotifier notifier;
    private Thread listener;
    private ServerListener serverListener;
    private BtVector btConnection;
    private LocalDevice device;

    /**
     * Konstrukor inicjalizujacy pola klasy.
     */
    public Server() {
        UUID = new UUID("1101", true).toString();
        btConnection = new BtVector();
        try {
            device = LocalDevice.getLocalDevice();
        } catch (BluetoothStateException ex) {
            log(ex);
        }
    }

    /**
     * Metoda ustawia serwer w stan <code>DiscoveryAgent.GIAC</code> oraz
     * zaczynajaca oglaszanie sie serwera.
     * @param _serverListener obietk ktory bedzie nasluchiwal zdarzenie z obslugi
     * serwer, implementujaca <code>ClientListener</code>.
     */
    public void createServer(ServerListener _serverListener) {
        try {
            serverListener = _serverListener;
            device.setDiscoverable(DiscoveryAgent.GIAC);
            String url = "btspp://localhost:" + UUID + ";name=BtTank";
            log("Create server:\n" + url + "\n");
            notifier = (StreamConnectionNotifier) Connector.open(url);
        } catch (IOException e) {
            log(e);
        }
    }

    /**
     * Metoda po której serwer zacznie nasluchiwanie nowych polaczen.
     */
    public void publishServer() {
        listener = new Thread() {

            @Override
            public void run() {
                try {
                    while (true) {
                        log("Waiting for connection...");
                        addBtConnection(notifier.acceptAndOpen());
                    }
                } catch (IOException e) {
                    log(e);
                }
            }
        };
        listener.start();
    }

    /**
     * Metoda odpowiada za wysylanie danych do klienta.
     * @param data dane do wyslania
     * @param btAdress adres bluetooth uzytkownika, ktoremu chcemy wyslac dane
     */
    public void sendData(String data, String btAdress) {
        try {
            ((BtConnection) btConnection.get(findDevice(btAdress))).sendData(data);
        } catch (IndexOutOfBoundsException ex) {
            log(ex);
        }
    }

    private int findDevice(String btAdress) {
        for (int i = 0; i < btConnection.size(); i++) {
            if (((BtConnection) btConnection.get(i)).getBtAdress() == null ? btAdress == null : ((BtConnection) btConnection.get(i)).getBtAdress().equals(btAdress)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Metoda zaprzestajaca oglaszanie sie serwera.
     */
    public void stopPublishingServer() {
        try {
            notifier.close();
            listener = null;
            log("Server listener down.");
        } catch (IOException ex) {
            log(ex);
        }
    }

    private void addBtConnection(StreamConnection conn) {
        btConnection.add(new BtConnection(conn, this));
        serverListener.clientConnected(((BtConnection) btConnection.get(btConnection.size() - 1)).getBtAdress());
    }

    /**
     * Motoda oblugujaca zerwanie polaczenia.
     * @param btAdress adres odlaczonego urzadzenia
     */
    public void connectionLost(String btAdress) {
        btConnection.remove(findDevice(btAdress));
        serverListener.connectionLost(btAdress);
    }

    /**
     * Metoda oblsugujaca o otrzymanie nowych danych.
     * @param data otrzymane nowe dane
     * @param btAdress adres clienta od ktorego zostaly przeslane dane
     */
    public void dataReceived(String data, String btAdress) {
        serverListener.dataReceived(data, btAdress);
    }

    private void log(String msg) {
        System.out.println(msg);
    }

    private void log(Throwable e) {
        log(e.getMessage());
    }
}
